package com.join.template.html.expression;

import com.join.template.core.configuration.ClassConvertConfig;
import com.join.template.core.configuration.ExpressionConfig;
import com.join.template.core.context.Content;
import com.join.template.core.context.HashContext;
import com.join.template.core.expression.JoinExpression;
import com.join.template.core.factory.JoinFactory;

import com.join.template.core.util.Assert;
import com.join.template.core.util.DateUtil;
import com.join.template.core.util.TemplateException;
import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;

public class JexlExpression implements JoinExpression {
    private ClassConvertConfig baseType;
    private ExpressionConfig exprConfig;
    private Content context;
    private Expression expression;
    private final static JexlEngine jexlEngine = new JexlEngine();

    JexlContext jexlContext = new JexlContext() {
        @Override
        public Object get(String name) {
            return JexlExpression.this.context.get(name);
        }

        @Override
        public void set(String name, Object value) {
            JexlExpression.this.context.put(name, value);
        }

        @Override
        public boolean has(String name) {
            return JexlExpression.this.context.hasKey(name);
        }
    };


    @Override
    public void setJoinFactory(JoinFactory joinFactory) {
        this.exprConfig = joinFactory.getConfiguration().getExpression();
        this.baseType = joinFactory.getConfiguration().getBaseType();
    }

    @Override
    public void setExpression(String expression) {
        this.expression = jexlEngine.createExpression(expression);
    }

    @Override
    public void setContext(Content context) {
        this.context = context;
    }

    @Override
    public void put(String K, Object V) {
        if (context == null) {
            context = new HashContext();
        }
        context.put(K, V);
    }

    @Override
    public String evaluate() {
        Assert.isNull(this.expression, "请设置表达式");
        String value = "";
        Object evaluate = this.expression.evaluate(jexlContext);
        if (evaluate == null) {
            return null;
        }
        if (evaluate instanceof Integer
                || evaluate instanceof Integer
                || evaluate instanceof Long
                || evaluate instanceof BigInteger) {
            value = String.valueOf(evaluate);
        } else if (evaluate instanceof Double
                || evaluate instanceof Float
                || evaluate instanceof BigDecimal) {
            NumberFormat numberFormat = NumberFormat.getNumberInstance();
            numberFormat.setMaximumFractionDigits(baseType.getDecimalScale());
            value = numberFormat.format(evaluate);
        } else if (evaluate instanceof Date) {
            Date date = (Date) evaluate;
            value = DateUtil.getDate(baseType.getDateFormat(), date);
        } else if (evaluate instanceof LocalDateTime) {
            LocalDateTime local = (LocalDateTime) evaluate;
            value = DateUtil.getLocal(baseType.getDateTimeFormat(), local);
        } else if (evaluate instanceof LocalDate) {
            LocalDate local = (LocalDate) evaluate;
            value = DateUtil.getLocal(baseType.getDateFormat(), local);
        } else if (evaluate instanceof LocalTime) {
            LocalTime local = (LocalTime) evaluate;
            value = DateUtil.getLocal(baseType.getTimeFormat(), local);
        } else {
            value = evaluate.toString();
        }
        return value;
    }


    @Override
    public Boolean judge() {
        Object judge = this.expression.evaluate(jexlContext);
        if (judge == null || judge.getClass() != Boolean.class) {
            throw new TemplateException("错误的判断语法");
        }
        return (Boolean) judge;
    }


    ;
}
